<?php
/**
 * Description: 有赞订单
 * Author: hezhizheng
 * Date: 2018/7/13
 * Time: 15:25
 * Created by Created by Panxsoft.
 */

namespace App\Services\Youzan;

use App\Exceptions\ApiErrCode;
use App\Exceptions\ApiException;
use App\Http\Requests\Admin\OrderIndexRequest;
use App\Models\Concerns\YouzanAuthorizerable;
use App\Models\Customer\Customer;
use App\Models\Order;
use App\Models\Store\Store;
use App\Models\Youzan\YouzanAuthorizer;
use App\Services\Utils\Transaction;
use Illuminate\Pagination\LengthAwarePaginator;

class OrderRepository
{
    protected $youzanApp;

    public function __construct()
    {
        $this->youzanApp = new AppFactory;
    }

    /**
     *
     * 单个商品下单
     *
     * @test
     *
     * @param YouzanAuthorizer $youzanAuthorizer
     * @param array $params
     * @return array
     * @throws \Exception
     * @throws \Throwable
     */
    public function createTradeBillGood(YouzanAuthorizer $youzanAuthorizer, array $params)
    {
        $authorYouzan = $this->youzanApp->getAuthorYouzan($youzanAuthorizer);

        try {
            /** 返回的数据格式
             * @see Mock::generateOrder()
             */
            $result = $authorYouzan->request(AppFactory::YOUZAN_TRADE_BILL_GOOD_URL_GET, $params);

        } catch (\Exception $exception) {
            throw new ApiException(ApiErrCode::YOUZAN_ERROR,$exception->getMessage());
        }

        return $result;
    }

    /**
     * 多个商品下单 (找不到source，无法从有赞订单中查到)
     * @deprecated
     * @test
     *
     * @param YouzanAuthorizer $youzanAuthorizer
     * @param array $params
     * @return array
     * @throws \Exception
     * @throws \Throwable
     */
    public function createTradeBillGoods(YouzanAuthorizer $youzanAuthorizer, array $params)
    {
        $authorYouzan = $this->youzanApp->getAuthorYouzan($youzanAuthorizer);

        try {
            $result = $authorYouzan->request(AppFactory::YOUZAN_TRADE_BILL_GOODS_URL_GET, $params);
        } catch (\Exception $exception) {
            throw new ApiException(ApiErrCode::YOUZAN_ERROR,$exception->getMessage());
        }

        return $result;
    }

    /**
     * 获取多个订单
     *
     * @test
     *
     * @param YouzanAuthorizer $youzanAuthorizer
     * @param array $params
     * @return array
     * @throws \Exception
     * @throws \Throwable
     */
    public function getTradesSold(YouzanAuthorizer $youzanAuthorizer, array $params = [])
    {
        $authorYouzan = $this->youzanApp->getAuthorYouzan($youzanAuthorizer);

        try {
            $result = $authorYouzan->request(AppFactory::YOUZAN_TRADES_SOLD_GET, $params, [], '4.0.0');
        } catch (\Exception $exception) {
            throw new ApiException(ApiErrCode::YOUZAN_ERROR, $exception->getMessage());
        }

        return $result;
    }

    /**
     * @test
     *
     * 查询当个订单信息
     *
     * @param YouzanAuthorizer $youzanAuthorizer
     * @param array $params
     *
     * @return array
     * @throws \Exception
     * @throws \Throwable
     */
    public function getTrade(YouzanAuthorizer $youzanAuthorizer, array $params)
    {
        $authorYouzan = $this->youzanApp->getAuthorYouzan($youzanAuthorizer);

        try {
            /**
             * @see Mock::queryOrderDetail()
             */
            $result = $authorYouzan->request(AppFactory::YOUZAN_TRADE_GET, $params, [], '4.0.0');
        } catch (\Exception $exception) {
            throw new ApiException(ApiErrCode::YOUZAN_ERROR, $exception->getMessage());
        }

        return $result;
    }

    /**
     * @param int $page_no - 页数
     * @param int $page_size - 每页返回数量
     * @param string|null $start_created - 开始日期
     * @param string|null $end_created - 结束日期 （跨度最多3个月）
     *
     * @return array
     */
    public function assembleTradesSoldGetParams(int $page_no = 1,
                                                int $page_size = 100,
                                                string $start_created = null,
                                                string $end_created = null): array
    {
        return [
            'page_no'       => $page_no,
            'page_size'     => $page_size,
            'start_created' => $start_created ?: (string) now()->subMonth(3),
            'end_created'   => $end_created ?: (string) now(),
        ];
    }

    /**
     * @test
     *
     * @param YouzanAuthorizer $youzanAuthorizer
     * @param array $trades_sold_params
     *
     * @see assembleTradesSoldGetParams
     *
     * @return array
     *
     * @throws \Exception - 错误示例如下：
     *      每次可搜索3个月订单记录，请重新选择起止时间
     *      无订单列表
     * @throws \Throwable
     */
    public function getAllTradesSold(YouzanAuthorizer $youzanAuthorizer,
                                     array $trades_sold_params): array
    {
        $params = $trades_sold_params;
        $last_page = null;
        $orders = [];

        do {
            $trades_sold = $this->getTradesSold($youzanAuthorizer, $params);

            $orders = array_merge($orders, $trades_sold['full_order_info_list']);

            if (! $last_page) { // we should calculate total page after first request got 'total_results'
                $last_page = (int) (ceil($trades_sold['total_results'] / $params['page_size']));
            }
        } while ($params['page_no']++ < $last_page);

        /**
         * @see Mock::queryOrders()
         */
        return $orders;
    }

    /**
     * 测试协程用，测试有赞订单如果多了每一次分页查询有 200KB 以上的数据，可能要使用协程 foreach 过滤减少内存占用
     *
     * @param YouzanAuthorizer $youzanAuthorizer
     * @param array $params
     *
     * @return \Generator
     * @throws \Exception
     * @throws \Throwable

     */
    public function coGetAllTradesSoldOrders(YouzanAuthorizer $youzanAuthorizer,
                                              array $params): \Generator
    {
        do {
            $trades_sold = $this->getTradesSold($youzanAuthorizer, $params);

            yield $trades_sold['full_order_info_list'];
        } while ($params['page_no']++ < $trades_sold['total_results'] / $params['page_size']);
    }

    /**
     * 同步有赞订单到系统
     *
     * @param array $transformItems
     * @return mixed
     * @throws \Exception
     */
    public function syncOrders(array $transformItems)
    {
        $insert = Transaction::handle(function () use ($transformItems) {

            foreach ($transformItems as $transformItem) {
                (new Order)->updateOrCreate(
                    ['trad_no' => $transformItem['trad_no']],
                    $transformItem
                );
            }
            return true;
        });
        return $insert;
    }

    /**
     * 查询同步完成之后的orders表
     *
     * @param YouzanAuthorizerable|Customer|Store $customer_or_store
     * @param OrderIndexRequest $request
     * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
     */
    public function getOrdersForStoreIds(YouzanAuthorizerable $customer_or_store,
                                         OrderIndexRequest $request)
    {
        /** @var LengthAwarePaginator $orders */
        $orders = $customer_or_store->orders()
            ->filterKeyword($request->keyword)
            ->filterStoreId($request->store_id)
//            ->whereIn('store_id', $store_ids)
//            ->where('customer_id',$customer_id)
            ->with(['customer', 'store', 'deviceAccount.device'])
            ->latest()
            ->paginate($request->per_page ?: 15);

        $orders->transform(function (Order $order){
            $order->append('refund_state_str');
            $order->product_title = $order->product_json_data['product_title'] ?? null;
            $order->product_thumb_url = $order->product_json_data['product_thumb_url'] ?? null;

            return $order;
        });

        return $orders;
    }

    /**
     * 处理订单退款的状态 - 把退出类型/状态 整数 转成 字符串， 无退款状态则把空数组转成 null 给前端
     *
     * @see Mock::queryOrderDetail()
     * @param array $order_detail
     *
     * @return array
     */
    public function transformOrderRefundInfo(array $order_detail)
    {
        /**
         * 退款 $order_detail['refund_order'] 的数组结构参考
         *
         * @link https://www.youzanyun.com/apilist/structparam/response/StructurizationTradeRefundInfoDetail[]
         */
        if (isset($order_detail['refund_order']) && count($order_detail['refund_order'])) {
            $ref_type = $order_detail['refund_order'][0]['refund_type'];
            $order_detail['refund_order'][0]['refund_type'] = Order::REFUND_TYPE_MAP[$ref_type] ?? '未知';

            $refund_state_id = $order_detail['refund_order'][0]['refund_state'];
            $order_detail['refund_order'][0]['refund_state'] = Order::ORDER_REFUND_DETAIL_STATE[$refund_state_id] ?? '未知';
        } else {
            $order_detail['refund_order'] = null;
        }

        return $order_detail;
    }
}